/*
Copyright SecureKey Technologies Inc. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package retry

import (
	"time"

	"git.cloud.inspur.com/ichain/ichain-protos-go/common"
	pb "git.cloud.inspur.com/ichain/ichain-protos-go/inode"
	"git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/common/errors/status"
	grpcCodes "google.golang.org/grpc/codes"
)

const (
	// DefaultAttempts 默认尝试次数
	DefaultAttempts = 3
	// DefaultInitialBackoff 默认基础回退时间
	DefaultInitialBackoff = 500 * time.Millisecond
	// DefaultMaxBackoff 最大的重试回退时间
	DefaultMaxBackoff = 60 * time.Second
	// DefaultBackoffFactor 默认回退系数
	DefaultBackoffFactor = 2.0
)

// 资源管理的默认重试配置
const (
	ResMgmtDefaultAttempts       = 5
	ResMgmtDefaultInitialBackoff = time.Second
	ResMgmtDefaultMaxBackoff     = 60 * time.Second
	ResMgmtDefaultBackoffFactor  = 2.5
)

// DefaultOpts 默认重试配置
var DefaultOpts = Opts{
	Attempts:       DefaultAttempts,
	InitialBackoff: DefaultInitialBackoff,
	MaxBackoff:     DefaultMaxBackoff,
	BackoffFactor:  DefaultBackoffFactor,
	RetryableCodes: DefaultRetryableCodes,
}

// DefaultChainOpts 请求链相关的重试配置
var DefaultChainOpts = Opts{
	Attempts:       DefaultAttempts,
	InitialBackoff: DefaultInitialBackoff,
	MaxBackoff:     DefaultMaxBackoff,
	BackoffFactor:  DefaultBackoffFactor,
	RetryableCodes: ChainClientRetryableCodes,
}

// DefaultResMgmtOpts 资源管理请求相关的重试配置
var DefaultResMgmtOpts = Opts{
	Attempts:       ResMgmtDefaultAttempts,
	InitialBackoff: ResMgmtDefaultInitialBackoff,
	MaxBackoff:     ResMgmtDefaultMaxBackoff,
	BackoffFactor:  ResMgmtDefaultBackoffFactor,
	RetryableCodes: ResMgmtDefaultRetryableCodes,
}

// DefaultRetryableCodes 重试错误码
var DefaultRetryableCodes = map[status.Group][]status.Code{
	status.EndorserClientStatus: {
		status.EndorsementMismatch,
		status.ContractNameNotFound,
	},
	status.EndorserServerStatus: {
		status.Code(common.Status_SERVICE_UNAVAILABLE),
		status.Code(common.Status_INTERNAL_SERVER_ERROR),
	},
	status.ConsenserServerStatus: {
		status.Code(common.Status_SERVICE_UNAVAILABLE),
		status.Code(common.Status_INTERNAL_SERVER_ERROR),
	},
	status.EventServerStatus: {
		status.Code(pb.TxValidationCode_DUPLICATE_TXID),
		status.Code(pb.TxValidationCode_ENDORSEMENT_POLICY_FAILURE),
		status.Code(pb.TxValidationCode_MVCC_READ_CONFLICT),
		status.Code(pb.TxValidationCode_PHANTOM_READ_CONFLICT),
	},
	// TODO: gRPC introduced retries in v1.8.0. This can be replaced with the
	// gRPC fail fast option, once available
	status.GRPCTransportStatus: {
		status.Code(grpcCodes.Unavailable),
	},
}

var ResMgmtDefaultRetryableCodes = map[status.Group][]status.Code{
	status.EndorserClientStatus: {
		status.ConnectionFailed,
		status.EndorsementMismatch,
		status.ContractNameNotFound,
	},
	status.EndorserServerStatus: {
		status.Code(common.Status_SERVICE_UNAVAILABLE),
		status.Code(common.Status_INTERNAL_SERVER_ERROR),
	},
	status.ConsenserServerStatus: {
		status.Code(common.Status_SERVICE_UNAVAILABLE),
		status.Code(common.Status_INTERNAL_SERVER_ERROR),
		status.Code(common.Status_BAD_REQUEST),
		status.Code(common.Status_NOT_FOUND),
	},
	status.EventServerStatus: {
		status.Code(pb.TxValidationCode_DUPLICATE_TXID),
		status.Code(pb.TxValidationCode_ENDORSEMENT_POLICY_FAILURE),
		status.Code(pb.TxValidationCode_MVCC_READ_CONFLICT),
		status.Code(pb.TxValidationCode_PHANTOM_READ_CONFLICT),
	},
	// TODO: gRPC introduced retries in v1.8.0. This can be replaced with the
	// gRPC fail fast option, once available
	status.GRPCTransportStatus: {
		status.Code(grpcCodes.Unavailable),
	},
}

var ChainClientRetryableCodes = map[status.Group][]status.Code{
	status.EndorserClientStatus: {
		status.ConnectionFailed, status.EndorsementMismatch,
		status.Code(pb.TxValidationCode_MVCC_READ_CONFLICT),
		status.ContractNameNotFound,
	},
	status.EndorserServerStatus: {
		status.Code(common.Status_SERVICE_UNAVAILABLE),
		status.Code(common.Status_INTERNAL_SERVER_ERROR),
		status.PvtDataDisseminationFailed,
	},
	status.ConsenserClientStatus: {
		status.ConnectionFailed,
	},
	status.ConsenserServerStatus: {
		status.Code(common.Status_SERVICE_UNAVAILABLE),
		status.Code(common.Status_INTERNAL_SERVER_ERROR),
	},
	status.EventServerStatus: {
		status.Code(pb.TxValidationCode_DUPLICATE_TXID),
		status.Code(pb.TxValidationCode_ENDORSEMENT_POLICY_FAILURE),
		status.Code(pb.TxValidationCode_MVCC_READ_CONFLICT),
		status.Code(pb.TxValidationCode_PHANTOM_READ_CONFLICT),
	},
	// TODO: gRPC introduced retries in v1.8.0. This can be replaced with the
	// gRPC fail fast option, once available
	status.GRPCTransportStatus: {
		status.Code(grpcCodes.Unavailable),
	},
}

var ChainConfigRetryableCodes = map[status.Group][]status.Code{
	status.EndorserClientStatus: {status.EndorsementMismatch},
}
